lexer grammar CLexer;

options {
    language = Java;
}

tokens {
    REAL;
    ENTERO;
}

@lexer::header {
    package pl.CLexer;
    import java.util.HashMap;
    import java.io.IOException;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.BufferedReader;
}

@lexer::members {

    //Atributo de la clase analizador lexico que nos permitira
    //resolver los nombres de los tokens.
    TokenCollection to = new TokenCollection();

    /*Clase para resolver los nombre de los Tokens del analizador lexico*/
    public class TokenCollection {
    
        HashMap<Integer, String> tokensNamesAndTypes = null;
        
        public TokenCollection() {
            tokensNamesAndTypes = new HashMap<Integer, String>();
            String nombreFicheroTokens = getGrammarFileName().substring(0, getGrammarFileName().length() - 1) + "tokens";
            inicializarTokens(nombreFicheroTokens);
        }

        public void inicializarTokens(String nombreFicheroTokens) {
            try {
                FileReader fr = new FileReader(nombreFicheroTokens);
                BufferedReader br = new BufferedReader(fr);
                String line = "";
                String name = "";
                Integer number = -1;
                while ((line = br.readLine()) != null) {
                    name = line.substring(0, line.indexOf('='));
                    number = Integer.parseInt(line.substring(line.indexOf('=') + 1, line.length()));
                    tokensNamesAndTypes.put(number, name);
                }
                fr.close();
            } catch (FileNotFoundException fnfe) {
                System.out.println("Error fichero de tokens no encontrado: " + nombreFicheroTokens);
            } catch (IOException ioe) {
                System.out.println("Error al leer fichero de tokens: " + nombreFicheroTokens);
            }
        }
        
        public String getTokenName(Integer i) {
            return tokensNamesAndTypes.get(i);
        }
    
    }
    /*Clase para resolver los nombre de los Tokens del analizador lexico*/

    /*Clase propia MyToken para manejar los tokens a mis necesidades*/
    public class MyToken extends CommonToken {

        int tipoT;
        String nombreT;
        String lexemaT;
        int lineaT;
        int columnaT;

        public MyToken(CharStream input, int type, int channel, int start, int stop) {
            super(input, type, channel, start, stop);
            tipoT = -1;
            nombreT = "";
            lexemaT = "";
            lineaT = -1;
            columnaT = -1;
        }

        public void setAtributos(String lexema, int linea, int columna) {
            tipoT = getType();
            nombreT = to.getTokenName(tipoT);
            lexemaT = lexema;
            lineaT = linea;
            columnaT = columna;
        }

        @Override
        public String toString() {
            String tipo = Integer.toString(tipoT);
            String linea = Integer.toString(lineaT);
            String columna = Integer.toString(columnaT);
            return '[' + tipo + ", " + nombreT + ", [" + lexemaT + "], " + linea + ", " + columna + ']';
        }

    }
    /*Clase propia MyToken para manejar los tokens a mis necesidades*/

    //Metodo del analizador lexico sobrescrito para que se emita un objeto de
    //la clase MyToken
    @Override
    public Token emit() {
        MyToken my = new MyToken(input, state.type, state.channel, state.tokenStartCharIndex, getCharIndex() - 1);
        my.setAtributos(getText(), getLine(), getCharPositionInLine() - (getText().length() - 1));
        emit(my);
        return my;
    }

    //Lista que guarda los errores que se van produciendo durante el analisis lexico
    List<RecognitionException> errors = new ArrayList<RecognitionException>();

    //Metodo que devuelve la lista de errores que se han producido
    public List<RecognitionException> getAllErrors() {
        return errors;
    }
    
    //Metodo que comprueba si se han producido errores
    public boolean hasErrors() {
        return !errors.isEmpty();
    }

    //Metodo del analizador lexico sobrescrito para que los errores que se van
    //produciendo durante el analisis lexico en vez de mostrarse por consola
    //se guarden en la lista de errores (errors)
    @Override
    public void reportError(RecognitionException e) {
        displayRecognitionError(this.getTokenNames(), e);
        errors.add(e);
    }

}

// Analizador Lexico
VOID:                           'void';
INT:                            'int';
CHAR:                           'char';
FLOAT:                          'float';

WHILE:                          'while';
IF:                             'if';
BREAK:                          'break';

fragment NUEVALINEA_WINDOWS:    '\r\n';
fragment NUEVALINEA_LINUX:      '\n';
fragment TABULADOR:             '\t';
fragment ESPACIO:               ' ';
DELIMITADORES:                  (NUEVALINEA_WINDOWS | NUEVALINEA_LINUX | TABULADOR | ESPACIO)+ { skip(); };

fragment DIGITO:                '0'..'9';
fragment PUNTO_DECIMAL:         '.';
NUMERO:                         (DIGITO+ PUNTO_DECIMAL) => DIGITO+ PUNTO_DECIMAL DIGITO+ { $type = REAL; } |
                                                           DIGITO+ { $type = ENTERO; };

fragment LETRA:                 'a'..'z';
ID:                             LETRA (DIGITO | LETRA)*;

fragment COMILLA_DOBLE:         '"';
CADENA:                         COMILLA_DOBLE (~(COMILLA_DOBLE))* COMILLA_DOBLE;

fragment COMILLA_SIMPLE:        '\'';
LITERAL:                        COMILLA_SIMPLE ~(COMILLA_SIMPLE) COMILLA_SIMPLE;
         
PUNTO_COMA:                     ';';
COMA:                           ',';

MAS:                            '+';
MENOS:                          '-';
MULT:                           '*';

AND_LOGICO:                     '&&';
OR_LOGICO:                      '||';
NEG_LOGICO:                     '!';

MENOR:                          '<';
MAYOR:                          '>';
MAYOR_IGUAL:                    '>=';
IGUAL:                          '=';

INTERROGACION_CERRADA:          '?';
DOS_PUNTOS:                     ':';

PARENTESIS_ABIERTO:             '(';
PARENTESIS_CERRADO:             ')';

LLAVE_ABIERTA:                  '{';
LLAVE_CERRADA:                  '}';